package jwtx

import (
	"encoding/json"
	"errors"
	"net/http"
	"time"

	"github.com/duke-git/lancet/v2/convertor"
	"github.com/golang-jwt/jwt/v4"
	"github.com/zeromicro/go-zero/rest/token"
)

type Jwt struct {
	r          *http.Request
	secret     string
	prevSecret string
	ciaims     jwt.MapClaims
	err        error
}

func New(r *http.Request, secret, prevSecret string) *Jwt {
	ret := &Jwt{
		r:          r,
		secret:     secret,
		prevSecret: prevSecret,
	}

	ret.getJwtPayload()
	return ret
}

func (j *Jwt) getJwtPayload() {
	tokenData, err := token.NewTokenParser().ParseToken(j.r, j.secret, j.prevSecret)
	if err != nil {
		j.err = err
		return
	}

	if !tokenData.Valid {
		j.err = errors.New("token validation failed")
		return
	}

	claims, ok := tokenData.Claims.(jwt.MapClaims)
	if !ok {
		j.err = errors.New("get token failed")
		return
	}
	j.ciaims = claims
}

func (j *Jwt) Err() error {
	return j.err
}

func (j *Jwt) GetString(name string) (string, error) {
	if j.err != nil {
		return "", j.err
	}
	switch v := j.ciaims[name].(type) {
	case json.Number:
		return v.String(), nil
	case string:
		return v, nil
	}
	return "", errors.New("parse token failed")
}

func (j *Jwt) GetInt(name string) (int64, error) {
	if j.err != nil {
		return 0, j.err
	}
	switch v := j.ciaims[name].(type) {
	case json.Number:
		return v.Int64()
	case string:
		return convertor.ToInt(v)
	}
	return 0, errors.New("parse token failed")
}

func (j *Jwt) GetFloat(name string) (float64, error) {
	if j.err != nil {
		return 0, j.err
	}
	switch v := j.ciaims[name].(type) {
	case json.Number:
		return v.Float64()
	case string:
		return convertor.ToFloat(v)
	}
	return 0, errors.New("parse token failed")
}

// 生成jwt token
func GetJwtToken(secretKey string, seconds int64, payload map[string]any) (string, error) {
	iat := time.Now().Unix()
	claims := make(jwt.MapClaims)
	for name, val := range payload {
		claims[name] = val
	}
	claims["exp"] = iat + seconds
	claims["iat"] = iat
	tokenData := jwt.New(jwt.SigningMethodHS256)
	tokenData.Claims = claims
	return tokenData.SignedString([]byte(secretKey))
}
